import asyncio
from typing import List

import schemas
from exceptions import FieldCheckError, ModelReferenceError
from ioservice.business import find_businesses_by_query, count_businesses, \
    bulk_insert_businesses, update_one_business, delete_many_businesses_by_ids, aggregate_businesses
from ioservice.common.mongo import lookup_oid_reference
from ioservice.const import institution_col
from ioservice.institution import check_institution_exist_by_ids
from models.business import Business
from models.institution import Institution
from tasks.computing import alumni as alumni_computing
from utils.dbutil import get_sort_stages_for_order, LookupConfig, get_lookup_stages


async def search_by_agg(query, params: schemas.SearchParam):
    pipeline = [{"$match": query}]
    pipeline.extend(get_sort_stages_for_order(params.sort))
    pipeline.extend([
        {"$skip": params.offset}, {"$limit": params.size}
    ])
    inst_lookup = LookupConfig(from_="institution", local_field="institution_id", foreign_field="_id", as_field="institution",
                               fields=["institution_name", "is_deleted"], ref_type="object")
    pipeline.extend(get_lookup_stages([inst_lookup]))
    items = await aggregate_businesses(pipeline)
    return items


async def search_business_by_params(params: schemas.SearchParam):
    query = params.get_mongo_query(schemas.BusinessFilter)
    if params.current:
        params.initial_pagination()
    special_order_fields = {"start_time", "end_time"}
    total = await count_businesses(query)
    if params.sort and params.sort[0] in special_order_fields:
        items = await search_by_agg(query, params)
    else:
        items = await find_businesses_by_query(query, params.sort, params.offset, params.size, None)
        await lookup_oid_reference(items, "institution_id", "institution", institution_col, Institution, ["institution_name", "is_deleted"])
    return schemas.SearchResult(items=items, total=total)


async def bulk_check_businesses(businesses: List[Business]):
    inst_ids = {str(b.institution_id) for b in businesses if b.institution_id}
    missing = await check_institution_exist_by_ids(list(inst_ids))
    if missing:
        error = FieldCheckError(field="institution_id", error=f"There are non existent ids: {', '.join(missing)}")
        raise ModelReferenceError("business", [error])


async def bulk_insert_businesses_with_checking(businesses: List[Business]):
    await bulk_check_businesses(businesses)
    return await bulk_insert_businesses(businesses)


async def update_one_business_with_checking(business_id, business: Business):
    await bulk_check_businesses([business])
    result = await update_one_business(business_id, business)
    # 注意：用户如果清空输入，则传入值为null，所以不能exclude_none=True
    update_fields = list(business.dict(exclude_unset=True).keys())
    if 'priority' in update_fields:
        asyncio.ensure_future(alumni_computing.after_update_business(business_id))
    return result


async def delete_business(business_ids: List[str]):
    result = await delete_many_businesses_by_ids(business_ids)
    asyncio.ensure_future(alumni_computing.after_delete_businesses(business_ids))
    return result
